package cn.caplike.demo.repository.javassist;

import javassist.*;

public class CreatePerson {

    /**
     * Description: 创建一个 Person 的 class 文件.
     * @author LiKe
     * @date 2023-06-18 09:52:47
     */
    public static Object createPerson() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 1. 创建一个空类
        CtClass personClazz = pool.makeClass("cn.caplike.demo.repository.javassist.Person");

        // 2. 新增一个字段 private String name;
        // 字段名为name
        CtField param = new CtField(pool.get("java.lang.String"), "name", personClazz);
        // 访问级别是 private
        param.setModifiers(Modifier.PRIVATE);
        // 初始值是 "xiaoming"
        personClazz.addField(param, CtField.Initializer.constant("LiKe"));

        // 3. 生成 getter、setter 方法
        personClazz.addMethod(CtNewMethod.setter("setName", param));
        personClazz.addMethod(CtNewMethod.getter("getName", param));

        // 4. 添加无参的构造函数
        CtConstructor cons = new CtConstructor(new CtClass[]{}, personClazz);
        cons.setBody("{name = this.name;}");
        personClazz.addConstructor(cons);

        // 5. 添加有参的构造函数
        cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, personClazz);
        // $0=this / $1,$2,$3... 代表方法参数
        cons.setBody("{$0.name = $1;}");
        personClazz.addConstructor(cons);

        // 6. 创建一个名为printName方法，无参数，无返回值，输出name值
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, personClazz);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");
        personClazz.addMethod(ctMethod);

        //这里会将这个创建的类对象编译为.class文件
        personClazz.writeFile(getClassPath());

        return personClazz.toClass().newInstance();
    }

    public static void main(String[] args) {
        try {
            final Object instance = createPerson();
            System.out.println(instance.getClass().getName());
            // 1.调用生成类: 反射
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getClassPath() {
        return CreatePerson.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    }
}
